#pragma once

#include <functional>
#include "Socket.hpp"
#include "Log.hpp"

extern Log lg;
// 参数类型要匹配，即记得加上&,否则在ServerCalculator.cc中传参构造TcpServer时会报类型不匹配的错误
using func_t = std::function<std::string(std::string &)>;

class TcpServer
{
public:
    TcpServer(uint16_t port, func_t callback)
        : port_(port), callback_(callback)
    {
    }
    void Initial()
    {
        listenfd_.CreateSock();
        listenfd_.Bind(port_);
        listenfd_.Listen();
        lg(Info, "server init success"); // 创建成功，增加提示消息
    }

    void Start()
    {
        std::string client_ip;
        uint16_t client_port;
        while (true)
        {
            int fd = listenfd_.Accept(&client_ip, &client_port);
            if (fd < 0)
                continue;
            lg(Info, "accept success, client_ip: %s, client_port: %d", client_ip.c_str(), client_port);
            if (fork() == 0)
            {
                listenfd_.Close();
                std::string in_string;
                char in_buff[1024];
                while (true)
                {
                    ssize_t n = read(fd, in_buff, sizeof(in_buff));
                    if (n > 0)
                    {
                        in_buff[n] = 0;
                        in_string += in_buff;
                        std::string ret = callback_(in_string);
                        if (ret.empty())
                            continue;
                        n = write(fd, ret.c_str(), ret.size());
                        if (n == 0)
                            lg(Warning, "fd:%d write error, server accept quit", fd);
                        else if (n < 0)
                            lg(Warning, "fd:%d write error");
                    }
                    else if (n == 0)
                    {
                        lg(Warning, "fd:%d read error, server accept quit", fd);
                        break;
                    }
                    else
                    {
                        lg(Warning, "fd:%d read error, errno: %d, error string: %s trying again...", fd, errno, strerror(errno));
                    }
                }
                close(fd);
                exit(0);
            }
            close(fd);
        }
    }

private:
    Socket listenfd_;
    uint16_t port_;
    func_t callback_;
};